package cn.thoughtworks.school.services;

import cn.thoughtworks.school.entities.Serial;
import cn.thoughtworks.school.entities.User;
import cn.thoughtworks.school.exceptions.BusinessException;
import cn.thoughtworks.school.repositories.SerialRepository;
import cn.thoughtworks.school.response.SerialResponse;
import cn.thoughtworks.school.response.SimpleSerialResponse;
import cn.thoughtworks.school.response.SimpleVideoResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class SerialService {
  @Autowired
  private SerialRepository serialRepository;
  @Autowired
  private VideoService videoService;
  @Autowired
  private SubscriptionService subscriptionService;
  @Autowired
  private UserCenterService userCenterService;

  public List<Serial> getSerialsByCreatorId(Long userId) {
    return serialRepository.findByCreatorId(userId);
  }

  @Transactional
  public void deleteSerial(Long id) throws BusinessException {
    Serial serial = getSerialOrThrows(id);
    videoService.removeVideosFromSerial(serial);
    serialRepository.delete(serial);
  }

  private Serial getSerialOrThrows(Long id) throws BusinessException {
    return serialRepository.findById(id).orElseThrow(() -> new BusinessException("系列不存在"));
  }

  public Serial createSerial(Serial serial) {
    return serialRepository.save(serial);
  }

  public SerialResponse getSerial(Long id, User currentUser) throws BusinessException {
    Serial serial = getSerialOrThrows(id);
    Map creator = userCenterService.getUserInfo(serial.getCreatorId());
    List<SimpleVideoResponse> simpleVideoResponses = videoService.mergeVideosAndUsers(serial.getVideos());
    boolean isCurrentUserNeedSubscribe = isNeedSubscribe(currentUser, serial);
    return SerialResponse.build(creator, serial, isCurrentUserNeedSubscribe,simpleVideoResponses);
  }

  private boolean isNeedSubscribe(User currentUser, Serial serial) {
    if (serial.getIsPublic() || currentUser.getId().equals(serial.getCreatorId())) {
      return false;
    }
    return subscriptionService.isUserSubscribeSerial(currentUser.getId(), serial.getId());
  }

  public Serial editSerial(Long id, Serial serial) throws BusinessException {
    Serial foundSerial = getSerialOrThrows(id);
    foundSerial.update(serial);
    return serialRepository.save(foundSerial);
  }

  public List<SimpleSerialResponse> getTopSerials(Long id) {
    Long userCurrentOrganizationId = userCenterService.getUserCurrentOrganizationId(id);
    List<Serial> serials = serialRepository.findFirst8ByIsAllVisibleIsTrueAndIsReleaseIsTrueOrOrganizationIdAndIsReleaseIsTrueOrderByCreateTimeDesc(userCurrentOrganizationId);
    return mergeSerialsAndUsers(serials);
  }

  private List<SimpleSerialResponse> mergeSerialsAndUsers(List<Serial> serials) {
    List<Map> users = getUsers(serials);
    return serials.stream()
      .map(video -> {
        Map user = users.stream().filter(item -> Objects.equals(video.getCreatorId(), Long.parseLong(item.get("id").toString())))
          .findFirst().get();
        return SimpleSerialResponse.build(user, video);
      })
      .collect(Collectors.toList());
  }

  private List<Map> getUsers(List<Serial> serials) {
    List<Long> userIds = serials.stream()
      .map(Serial::getCreatorId)
      .collect(Collectors.toList());

    return userCenterService.getUsersByIds(userIds);
  }

  public PageImpl getPageableVideosByOrganization(Long id, Pageable pageable) {
    Long userCurrentOrganizationId = userCenterService.getUserCurrentOrganizationId(id);
    Page<Serial> serialPage = serialRepository.findByIsAllVisibleIsTrueAndIsReleaseIsTrueOrOrganizationIdAndIsReleaseIsTrueOrderByCreateTimeDesc(userCurrentOrganizationId, pageable);

    List<Serial> serials = serialPage.getContent();
    List<SimpleSerialResponse> simpleVideoResponses = mergeSerialsAndUsers(serials);
    return new PageImpl(simpleVideoResponses, pageable, serialPage.getTotalElements());
  }

  public List<SimpleSerialResponse> getMySubscriptionSerials(Long id) {
    List<Serial> serials = serialRepository.getMySubscriptionSerials(id);
    return mergeSerialsAndUsers(serials);
  }
}
